// TSX10.java

package com.ibm.tspaces.examples.x10;


/*
**  Licensed Materials - Property of IBM
**
**  (C) COPYRIGHT IBM Corp. 1996, 1997  All rights reserved
**
**  US Government Users Restricted Rights - Use, duplication or
**  disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
**
**/

import  java.util.*;
import  java.text.*;
import  java.io.*;

import  com.ibm.tspaces.*;
import  com.ibm.tspaces.ac.*;
import  java.security.Principal;
import  java.security.acl.*;


/**
** This class will take care of reciving X10Events from 
** the X10Monitor object and placing the corresponding information 
** into a TupleSpace..
**
** The main() method for this class will start up an instance of 
** X10Monitor for a specified comm port.
**   
** @see X10
** @see TupleSpace
** @see X10Monitor
** @version $Revision: 2.1.2.1 $ $Date: 2000/01/28 18:20:48 $
** @author John Thomas
*/

public class TSX10 
	implements X10EventListener, Runnable, Callback, 
	           AclFactory.DefaultPermissions  {

	
Hashtable    _devices  = new Hashtable();
	
private static TupleSpace   _ts     = null;		

X10Monitor  _x10 = null;
	           	
  /***********************************************************
  ** TSX10 Constructor **
  ******************************/  
	/**
	** Default constructor.
	**   
	*/
public 
TSX10()   {
	
}

	/***********************************************************
  ** setupX10 **
  ******************************/  
  /**
  ** This will do the setup of the X10Monitor used by TSX10
  **   
  */
void
setupX10(String port,String configFile )  throws X10MonitorException  {
  try {
    _x10 = new X10Monitor(port,configFile);
    Thread t = new Thread(_x10,"X10Mon");
    t.start();
    _x10.addX10EventListener(this);
  } catch (Exception e) {
  	
    Debug.out(e);
  	throw new X10MonitorException(e.getMessage());
  }
} // end setupX10


	/***********************************************************
  ** setupTS **
  ******************************/  
  /**
  ** This will do the setup of the TupleSpace used by TSX10
  **   
  */
void
setupTS(String MyHost, String MyTsName,String MyUserid,String MyPassword)  {

  _ts = null;
  try {
    Debug.out("T Spaces Version = " + TupleSpace.getVersion());
  	TupleSpace.setUserName(MyUserid);
  	TupleSpace.setPassword(MyPassword);
    boolean exists = TupleSpace.exists(MyTsName,MyHost);
    
    if ( exists ) {
      Debug.out("TupleSpace "+MyTsName+" already created.");
      
      _ts = new TupleSpace(MyTsName,MyHost);
             

    } else {
      // Create a Tuple describing the configuration
      Debug.out("Create TupleSpace "+MyTsName+" with "+MyUserid+" as owner.");

      Tuple config = new Tuple();
      config.add( new Field(TupleSpace.PERSISTENCE, new Boolean( true)) );
      
      // Create a Tuple describing the permissions 
      String owner;
      if (MyUserid != null)
        owner = MyUserid;
      else 
        owner = TupleSpace._DEFAULT_USER;
      
      //    Group Users  - Read access 
      //    User anonymous  - No Read Access
      //    User owner      - READ and Write
      // Note:
      //   This will affect only the initial creating of the space.
      //
      AclEntry ae1 = AclFactory.createAclEntry("Users",P_READ);
      //AclEntry ae2 = AclFactory.createAclEntry("anonymous",P_READ);
      //ae2.setNegativePermissions();
      AclEntry ae3 = AclFactory.createAclEntry(owner,new Permission[] {P_READ,P_WRITE});
      
      Acl myacl = AclFactory.createAcl(
            MyTsName,owner,
            new AclEntry[] {ae1,ae3});
      Tuple permission = new Tuple((Serializable)myacl);
      
      Debug.out(myacl);
      Debug.out("Attempting to open TupleSpace "+MyTsName+"@"+MyHost);
      // Create the new Space 
      
      _ts = new TupleSpace(MyTsName,
                MyHost,TupleSpace.DEFAULTPORT,
                config,permission);
                //MyUserid,MyPassword);
    } // else  

  
  
 
    // Get the level of the TSpace client support
    Debug.out("T Spaces Version = " + TupleSpace.getVersion());
    
    //
    // Start from scratch by deleting everything there.
    //  Later we will not want to do this.
    _ts.deleteAll();	
	}  catch(Exception tse) {
    Debug.out( tse);  
    
  }   
}  //end setupTS
	           	
	           	
  /***********************************************************
  ** functionPerformed **
  ******************************/  
  /**
  ** The functionPerformed method will process X10Events sent 
  ** to objects that have registered with X10Monitor.addX10EventListener.
  ** 
  ** It will process the 2 types of X10Event s
  **  DEVICE_DEFINE 
  **    This event is sent when a new X10 device is discovered by X10Monitor.
  **
  **  DEVICE_STATUS 
  **     This event is sent when a new functioncode is reported for a 
  **     X10Device.  
  **
  */
		
public void
functionPerformed(X10Event x10Event) {
	Debug.out("TSX10 Received: "+x10Event);
	try {
      
    X10Device dev = x10Event.getDevice();
    int id   = x10Event.getID();
		String address = dev.getAddress();
    String name = dev.getName();
		Integer type = new Integer(dev.getType());
		String desc = dev.getDescription();
    
    if (id == X10Event.DEVICE_DEFINE) {
      // Device_define event
    	// Write a Tuple describing the device to the space
      Tuple tuple = new Tuple("X10Device",address,name,type,desc);
    	FieldPS devps = new FieldPS(dev);
    	tuple.add(devps);
    	_ts.write(tuple);
    } else if (id == X10Event.DEVICE_FUNCTION) {
    	// Device_function - command was issued for device
    	// Write Tuple describing new device status 
    	String status = dev.getStatus();
    	Tuple newtuple = new Tuple("X10Status",address,status);
      Tuple template = new Tuple("X10Status",address,new Field(String.class));
    	
    	Tuple found = _ts.read(template);
    	if (found == null) 
    		_ts.write(newtuple);
    	else 
    		_ts.update(found,newtuple);
    }
		
	} catch (Exception e) {
		Debug.out(e);
	}
}

  /***********************************************************
  ** run **
  ******************************/  
	/**
  ** Do we really need this?  
  **
  */
	
public void
run()   {
	try {
      // create a template to indicate what we are interested in
    Tuple match = new Tuple("X10Command",
              new Field(String.class),            
              new Field(String.class)  );
        // place ourselves on the list to notified when anyone anywhere
        // writes a matching Tuple to this TupleSpace.            
    int seqNum = _ts.eventRegister(TupleSpace.WRITE,match, this);
 

    
  } catch (Exception e) {
    Debug.out(e);
  }

	while(true) {
		X10.delay(2);
	}
		
}
		
		
 /*
***************************************************************************
** ** call **
** **********
*/
/**
** Process the callback from the server that notifies us when anyone 
** (including ourselves) writes an X10COMMAND tuple to the X10 TupleSpace.
** When we recieve a command, then it will be sent to the X10Monitor to 
** be executed by the X10 protocol.
** An example of an X10Command is:
**   [ "X10Command", "A1", "ON" ]
**
** @param eventName_ the name of the event that caused this call, 
**    It will be either TupleSpace.WRITE or TupleSpace.DELETE
** @param tsName_ the name of the tuple space this command was executed on.
** @param sequenceNumber_ the sequenceNumber for this event/command
** @param tuple_ the returned tuple or a Tuple with an exception inside
** @param isException_ was the command processed normaly or was there an exception
**
** @return true if this is the last call this sequence # should be getting otherwise false
*************************************************************************** 
*/
public synchronized boolean 
call(String eventName_, String tsName_, int sequenceNumber_, SuperTuple tuple_, 
                boolean isException_) {
    
  byte[] function = new byte[2];
  try {
  	 String on = (String)tuple_.getField(2).getValue();
  	function[0] = 0x06;   // Header for simple on/off function
     String address = (String)tuple_.getField(1).getValue();
  	 byte housecode = (byte)(X10.code2Bin(address) & 0xf0);
     if (on.equalsIgnoreCase("ON")) 
       function[1] = (byte)(X10._ON | housecode);  	
     else 
       function[1] = (byte)(X10._OFF | housecode);
     Debug.out("issue "+ address + " " + X10.Hex(function));
  	 _x10.issueCommand(address,function);
  	
   }  catch(Exception e) {
     Debug.out(e);
   } // catch
     
   return false;
} // call
    

  /***********************************************************
  ** main **
  ******************************/  
  /**
  ** This method gets control at startup.
  **
  ** It will parse the command line and setup the 
  ** required TupleSpace.
  ** 
  ** @param argv  Command line options.
  **   Syntax:  -D -h host -n tsname -p commport userid/password
  **
  **
  */
	
public static void 
main( String argv[] ) {
  String  MyHost  =  "localhost";
  String  MyCommPort = "COM1";
  String MyTsName = "X10";
  String MyUserid = TupleSpace._DEFAULT_USER;
  String MyPassword = "";
	String MyConfigFile = null;
  
  if (argv.length == 0) {
    System.out.println("Syntax:  [ -h ServerHost] [-n tsName] [-p commport] userid/password");
    System.out.println(" Using defaults: -h localhost -n X10 -p COM2");
  	// standard processing of operands
  }
	
  for (int i=0; i< argv.length; i++) {
    if (argv[i].equals("-D") ) {
      Debug.setDebugOn(true);
    } else if (argv[i].equals("-h")) {
      MyHost = argv[++i]; 
    } else if (argv[i].equals("-n")) {
      MyTsName = argv[++i];  
    } else if (argv[i].equals("-p")) {
      MyCommPort = argv[++i];  
	  } else if (argv[i].equals("-c")) {
      MyConfigFile = argv[++i];  

    } else   {
        MyUserid = argv[i];
        int n;
        if ((n =MyUserid.indexOf('/')) >0 ) {
          MyPassword = MyUserid.substring(n+1);
          MyUserid = MyUserid.substring(0,n);
        }
    }
  }

   
  
  Debug.out("Host="+MyHost+" CommPort="+MyCommPort+
            " TSName="+MyTsName+" User="+MyUserid+" Password="+MyPassword);
  	
  try { 
  	
	  TSX10 tsx10 = new TSX10();
  	
  	tsx10.setupTS(MyHost,MyTsName,MyUserid,MyPassword);
  	
  	tsx10.setupX10(MyCommPort,MyConfigFile);
  	
  	Thread t = new Thread(tsx10,"TSX10");
  	t.start();
  /******	
  	X10.delay(5);
  	Tuple tuple = new Tuple("X10Command","A3","On");
  	_ts.write(tuple);
  	  X10.delay(10);
    tuple = new Tuple("X10Command","A3","Off");
    _ts.write(tuple);
  ********/
    t.join();
      
  } catch(Exception tse) {
    Debug.out(0, tse);  
    
  }      
 
}  // end main()
	
} // end TSX10 class

/* $History: TSX10.java $ 
 * 
 * *****************  Version 4  *****************
 * User: Jthomas      Date: 3/23/99    Time: 9:28a
 * Updated in $/GCS/Development/TSpaces/Java/com/ibm/tspaces/examples/x10
 * minor cosmetic enhancements
 * 
 * *****************  Version 3  *****************
 * User: Jthomas      Date: 3/17/99    Time: 6:07p
 * Updated in $/GCS/Development/TSpaces/Java/com/ibm/tspaces/examples/x10
 * 
 * *****************  Version 2  *****************
 * User: Jthomas      Date: 3/16/99    Time: 9:41p
 * Updated in $/GCS/Development/TSpaces/Java/com/ibm/tspaces/examples/x10
 * minor fixes
*/
/* $Log: TSX10.java,v $
/* Revision 2.1.2.1  2000/01/28 18:20:48  jthomas
/* Add support for specifing the config file on command line
/*
/* Revision 2.1  1999/11/05 22:22:40  estesp
/* Update revision number to 2.1 on all files
/*
/* Revision 1.1.1.1  1999/11/05 16:24:54  estesp
/* Imported 2.1.0 release into Austin CVS
/*
 * Revision 1.3  1999/11/03 16:09:13  jthomas
 * Minor cleanup while testing with Jeode
 *
 * Revision 1.2  1999/06/17 05:39:48  thodes
 *   Updated the inital checkin to append the "Log" field and
 * convert CR/LF to CR (for win/unix interop).
 *
 *   The inital checkin lower-cased directrory names, and omitted
 * many dirs/files that will added later, e.g., MoDAL stuff.
 *
 * All of the above will need to be done to all VSS-to-CVS
 * converted sources.  In summary, in moving from VSS to CVS, do this:
 *  - lower-case dir names
 *  - CR/LF --> CR
 *  - append CVS "Log" entties
 *
 * (a script that helps with #2 and #3 is in
 *     tspaces1:~thodes/bin/update-keywords.sh)
 * 
 */

